package com.spring;

import com.spring.annotation.*;
import com.spring.pojo.BeanDefinition;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 请描述当前类的作用
 *
 * @author LSQ
 * @since 2023/2/13 19:34
 */
public class ApplicationContext {

    private Class configClass;

    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * 单例池
     */
    private Map<String, Object> singletonObjects = new HashMap<>();

    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();


    public ApplicationContext(Class configClass) {

        // 扫描
        scan(configClass);

        // 扫描完后需要去创建单例bean
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();

            if (!beanDefinition.getLazy() && "singleton".equals(beanDefinition.getScope())) {

                if (!singletonObjects.containsKey(beanName)) {
                    // 创建bean
                    Object bean = createBean(beanName, beanDefinition);
                    // 放入单例池
                    singletonObjects.put(beanName, bean);
                }

            }

        }

    }


    /**
     * 创建 bean 在这个过程中进行依赖注入
     *
     * @author LSQ
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition) {

        Class clazz = beanDefinition.getType();

        // 调用无参的构造方法
        Object instance = null;
        try {

            // 若要调用有参的构造方法 可以在 getConstructor() 这个方法里面传值
            instance = clazz.getConstructor().newInstance();

            // 遍历这个类的变量字段
            for (Field field : clazz.getDeclaredFields()) {

                // Spring源码中实现 @Autowired 是通过 BeanPostProcessor 来做的
                if (field.isAnnotationPresent(Autowired.class)) {
                    // 依赖注入
                    field.setAccessible(true);
                    // TODO 这里会存在循环依赖的问题
                    field.set(instance, getBean(field.getName()));
                }

            }


            // aware回调
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }


            /*if (clazz.isAnnotationPresent(Transactional.class)) {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(clazz);
                Object target = o;
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        System.out.println("开启事务");
                        Object result = method.invoke(target, objects);
                        System.out.println("提交事务");
                        return result;
                    }
                });
                o = enhancer.create();
            }*/


            // BeanPostProcessor 初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            // 初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }

            // BeanPostProcessor 初始化后
            // AOP就是在这里实现的！！！
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            }


        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return instance;
    }

    /**
     * 获取 bean
     *
     * @author LSQ
     */
    public Object getBean(String beanName) {
        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new NullPointerException("当前bean未定义！beanName:" + beanName);
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (!beanDefinition.getLazy() && "singleton".equals(beanDefinition.getScope())) {
            // 单例bean
            Object singletonBean = singletonObjects.get(beanName);
            if (singletonBean == null) {
                singletonBean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, singletonBean);
            }
            return singletonBean;
        } else {
            // 原型bean
            return createBean(beanName, beanDefinition);
        }
    }

    /**
     * 扫描 bean
     *
     * @author LSQ
     */
    private void scan(Class configClass) {
        // 获取要扫描bean的路径
        ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
        String scanPath = componentScan.value();
        scanPath = scanPath.replace(".", "/"); // com/lsq/service

        System.out.println("扫描bean的路径:" + scanPath);

        ClassLoader classLoader = ApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(scanPath);
        File file = new File(resource.getFile());

        // 遍历扫描路径下的所有类
        if (file.isDirectory()) {
            for (File f : file.listFiles()) {
                String absolutePath = f.getAbsolutePath();
                System.out.println(absolutePath);

                absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                absolutePath = absolutePath.replace("\\", ".");

                try {
                    // Spring源码中是使用ASM技术 根据class文件直接获取类元信息
                    Class<?> clazz = classLoader.loadClass(absolutePath);

                    // 判断类上是否存在 @Component 注解
                    if (clazz.isAnnotationPresent(Component.class)) {

                        // 判断是否为实现了 BeanPostProcessor 接口的类
                        // 注意 这里做判断的是一个类（class） 所以不能用 instanceof 关键字来做比较
                        if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                            BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.getConstructor().newInstance();
                            beanPostProcessorList.add(beanPostProcessor);
                        }


                        Component component = clazz.getAnnotation(Component.class);
                        String beanName = component.value();
                        if ("".equals(beanName)) {
                            // 默认beanName为首字母小写的类名
                            beanName = Introspector.decapitalize(clazz.getSimpleName());
                        }

                        // 创建beanDefinition
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setType(clazz);

                        // 再判断bean是单例的还是原型的
                        if (clazz.isAnnotationPresent(Scope.class)) {
                            Scope scope = clazz.getAnnotation(Scope.class);
                            String scopeValue = scope.value();

                            beanDefinition.setScope(scopeValue);
                        } else {
                            // 默认单例
                            beanDefinition.setScope("singleton");
                        }

                        // 判断是否为懒加载
                        beanDefinition.setLazy(clazz.isAnnotationPresent(Lazy.class));

                        // 存入beanDefinitionMap
                        beanDefinitionMap.put(beanName, beanDefinition);

                    }


                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }

            }
        }
    }

}
